#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/syscall.h>
#include "m_global.h"
#include "m_base.h"

int m_access(char *file)
{
    if (!access(file, F_OK | R_OK)) return 0;
    switch (errno)
    {
        case EACCES:
            fprintf(stderr, "The requested access would be denied to the file or search "
                    "permission is denied for one of the  directories  in  the path "
                    "prefix of pathname.  (See also path_resolution(2).)");
            break;
        case ELOOP:
            fprintf(stderr, "Too many symbolic links were encountered in resolving pathname.");
            break;
        case ENAMETOOLONG:
            fprintf(stderr, "pathname is too long.");
            break;
        case ENOENT:
            fprintf(stderr, "A directory component in pathname would have been "
                    "accessible but does not exist or was a dangling symbolic link.");
            break;
        case ENOTDIR:
            fprintf(stderr, "A component used as a directory in pathname is not, "
                    "in fact, a directory.");
            break;
        case EROFS:
            fprintf(stderr, "Write permission was requested for a file on a "
                    "read - only filesystem.");
        case EFAULT:
            fprintf(stderr, "pathname points outside your accessible address space.");
            break;
        case EINVAL:
            fprintf(stderr, "mode was incorrectly specified.");
            break;
        case EIO:
            fprintf(stderr, "An I / O error occurred.");
            break;
        case ENOMEM:
            fprintf(stderr, "Insufficient kernel memory was available.");
            break;
        case ETXTBSY:
            fprintf(stderr, "Write access was requested to an executable "
                    "which is being executed.");
            break;
        default:
            break;
    }
    return 1;
}

/* Read config file, strim space, tab and enter */
char* strtrim(char *src)
{
    char *tmp;
    int length = 0, chr = 0, tmp_chr = 0;
    length = strlen(src);
    if (!length)
        return NULL;

    tmp = (char *) malloc(length);
    memset(tmp, 0, length);

    for (chr = 0; chr < length; chr++)
    {
        if (src[chr] == 0x20 || src[chr] == 0x9 || src[chr] == 0x0a)
            continue;
        else
        {
            tmp[tmp_chr] = src[chr];
            tmp_chr++;
        }
    }
    memset(src, 0, length);
    strncpy(src, tmp, tmp_chr);
    free(tmp)
            ;
    length = strlen(src);
    if (length)
        return src;
    else
        return NULL;
}

int m_close_file(int fd)
{
    int ret;
    ret = close(fd);
    if (ret)
    {
        switch (errno)
        {
            case EBADF:
                fprintf(stderr, "fd isn’t a valid open file descriptor.\n");
                break;
            case EINTR:
                fprintf(stderr, "The close() call was interrupted by a signal.\n");
                break;
            case EIO:
                fprintf(stderr, "An I/O error occurred.\n");
                break;
            default:
                fprintf(stderr, "close fd, unknown error, errno is %d.\n", errno);
                break;
        }
        return ret;
    }
    return ret;
}

int m_open_file(char *file_name, int flags)
{
    int fd;
    fd = open(file_name, flags);
    if (-1 == fd)
    {
        fprintf(stderr, "Open %s error, errno is %d.\n", file_name, errno);

        switch (errno)
        {
            case EACCES:
                fprintf(stderr, "The  requested  access  to the file is not allowed, "
                        "or search permission is denied for one of the directories "
                        "in the path prefix of pathname, or the file did not exist "
                        "yet and write access to the  parent  directory  is  not  "
                        "allowed. (See also path_resolution(2).)\n");
                break;
            case EEXIST:
                fprintf(stderr, "pathname already exists and O_CREAT and O_EXCL "
                        "were used.\n");
                break;
            case EFAULT:
                fprintf(stderr, "pathname points outside your accessible address space.\n");
                break;
            case EISDIR:
                fprintf(stderr, "pathname refers to a directory and the access "
                        "requested involved writing (that is, O_WRONLY or O_RDWR is set).\n");
                break;
            case ELOOP:
                fprintf(stderr, "Too  many symbolic links were encountered in "
                        "resolving pathname, or O_NOFOLLOW was specified but "
                        "pathname was a sym-bolic link.\n");
                break;
            case EMFILE:
                fprintf(stderr, "The process already has the maximum number of files open.\n");
                break;
            case ENAMETOOLONG:
                fprintf(stderr, "pathname was too long.\n");
                break;
            case ENFILE:
                fprintf(stderr, "The system limit on the total number of open files "
                        "has been reached.\n");
                break;
            case ENODEV:
                fprintf(stderr, "pathname refers to a device special file and no "
                        "corresponding device exists.  (This is a Linux kernel bug; "
                        "in  this situation ENXIO must be returned.)\n");
                break;
            case ENOENT:
            case O_CREAT:
                fprintf(stderr, "is not set and the named file does not exist.  "
                        "Or, a directory component in pathname does not exist "
                        "or is a dangling symbolic link.\n");
                break;
            case ENOMEM:
                fprintf(stderr, "Insufficient kernel memory was available.\n");
                break;
            case ENOSPC:
                fprintf(stderr, "pathname was to be created but the device "
                        "containing pathname has no room for the new file.\n");
                break;
            case ENOTDIR:
                fprintf(stderr, "A component used as a directory in pathname "
                        "is not, in fact, a directory, or O_DIRECTORY was "
                        "specified and  pathname was not a directory.\n");
                break;
            case ENXIO:
            case O_NONBLOCK:
                fprintf(stderr, "is set, the named file is a FIFO and no process "
                        "has the file open for reading.  Or, the file is a device "
                        "special file and no corresponding device exists.\n");
                break;
            case EOVERFLOW:
                fprintf(stderr, "pathname refers to a regular file, too large to "
                        "be opened; see O_LARGEFILE above.\n");
                break;
            case EPERM:
                fprintf(stderr, "The O_NOATIME flag was specified, but the "
                        "effective user ID of the caller did not match the owner of "
                        "the  file  and the caller was not privileged (CAP_FOWNER).\n");
                break;
            case EROFS:
                fprintf(stderr, "pathname refers to a file on a read-only "
                        "filesystem and write access was requested.\n");
                break;
            case ETXTBSY:
                fprintf(stderr, "pathname refers to an executable image which is "
                        "currently being executed and write access was requested.\n");
                break;
            case EWOULDBLOCK:
                fprintf(stderr, "The O_NONBLOCK flag was specified, and an "
                        "incompatible lease was held on the file (see fcntl(2)).\n");
                break;
            default:
                fprintf(stderr, "unknown errno %d.\n", errno);
                break;
        }
        return fd;
    }
    return fd;
}

int get_file_sock(char *dir, char *file_name, int flags)
{
    char *full_path;
    int dir_length, file_name_length, i, j, fd;

    if (NULL == dir || NULL == file_name)
    {
        fprintf(stderr, "dir or file name not specified.\n");
        return -1;
    }

    dir_length = strlen(dir);
    file_name_length = strlen(file_name);
    full_path = (char *) malloc(dir_length + file_name_length + 1);
    if (NULL == full_path)
    {
        fprintf(stderr, "full_path malloc out of memory.\n");
        return -1;
    }
    memset(full_path, 0, dir_length + file_name_length + 1);

    if (0x2f == file_name[0])
        file_name++;

    for (i = 0; i < dir_length; i++)
        full_path[i] = dir[i];

    if (full_path[dir_length - 1] != 0x2f)
    {
        full_path[dir_length] = 0x2f;
        dir_length++;
    }

    for (i = dir_length, j = 0; i < dir_length + file_name_length; i++, j++)
        full_path[i] = file_name[j];

    fd = m_open_file(full_path, flags);

    return fd;
}

int m_read(int fd, char *buf, size_t count)
{
    int retval;
    retval = read(fd, buf, count);
    if (retval > -1)
        return retval;
    
    if (-1 == retval)
    {
        switch (errno)
        {

            case EAGAIN:
                fprintf(stderr, "Non-blocking I/O has been selected using "
                        "O_NONBLOCK and no data was immediately available "
                        "for reading.\n");
                break;
            case EBADF:
                fprintf(stderr, "fd is not a valid file descriptor or is "
                        "not open for reading.\n");
                break;
            case EFAULT:
                fprintf(stderr, "buf is outside your accessible address space.\n");
                break;
            case EINTR:
                fprintf(stderr, "The call was interrupted by a signal "
                        "before any data was read.\n");
                break;
            case EINVAL:
                fprintf(stderr, "fd is attached to an object which is "
                        "unsuitable for reading; or the file was opened  "
                        "with  the  O_DIRECT  flag,  and either  the  "
                        "address  specified  in  buf,  the  value specified "
                        "in count, or the current file offset is not "
                        "suitably aligned.\n");
                break;
            case EIO:
                fprintf(stderr, "I/O error. This will happen for example "
                        "when the process is in a background process group, "
                        "tries to  read  from  its controlling  tty, and "
                        "either it is ignoring or blocking SIGTTIN or its "
                        "process group is orphaned.  It may also occur when "
                        "there is a low-level I/O error while reading from "
                        "a disk or tape.\n");
                break;
            case EISDIR:
                fprintf(stderr, "fd refers to a directory.\n");
                break;
            default:
                break;
        }
    }
    return -1;
}

void* m_realloc(void *ptr, size_t size)
{
    ptr = realloc(ptr, size);
    if (NULL == ptr)
    {
        fprintf(stderr, "realloc size %lu unsuccess, not enough memory.\n", size);
        return NULL;
    }
    return ptr;
}

long long get_thread_id(int flag)
{
    long long thread_id = 0;
    switch(flag)
    {
        case LINUX_THREAD_ID:
            thread_id = syscall(__NR_gettid);
            break;
        case POSIX_THREAD_ID:
            thread_id = pthread_self();
            break;
        default:
            thread_id = -1;
            break;
    }
    return thread_id;
}
